package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.INCOMPATIBLE_TYPES;
import static wci.frontend.pascal.PascalErrorCode.INVALID_SUBRANGE_TYPE;
import static wci.frontend.pascal.PascalErrorCode.MIN_GT_MAX;
import static wci.frontend.pascal.PascalErrorCode.MISSING_DOT_DOT;
import static wci.frontend.pascal.PascalTokenType.DOT_DOT;
import static wci.frontend.pascal.PascalTokenType.IDENTIFIER;
import static wci.intermediate.typeimpl.TypeFormImpl.ENUMERATION;
import static wci.intermediate.typeimpl.TypeFormImpl.SUBRANGE;
import static wci.intermediate.typeimpl.TypeKeyImpl.SUBRANGE_BASE_TYPE;
import static wci.intermediate.typeimpl.TypeKeyImpl.SUBRANGE_MAX_VALUE;
import static wci.intermediate.typeimpl.TypeKeyImpl.SUBRANGE_MIN_VALUE;
import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalParserTD;
import wci.intermediate.TypeFactory;
import wci.intermediate.TypeSpec;
import wci.intermediate.symtabimpl.Predefined;

/**
 * <h1>SubrangeTypeParser</h1>
 *
 * <p>解析Pascal子界(subrange)</p>
 *
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class SubrangeTypeParser extends TypeSpecificationParser
{
    protected SubrangeTypeParser(PascalParserTD parent)
    {
        super(parent);
    }
    
    public TypeSpec parse(Token token)
        throws Exception
    {
        TypeSpec subrangeType = TypeFactory.createType(SUBRANGE);
        Object minValue = null; //下界
        Object maxValue = null; //上界

        //解析下界常量
        Token constantToken = token;
        ConstantDefinitionsParser constantParser =
            new ConstantDefinitionsParser(this);
        minValue = constantParser.parseConstant(token);
        // 设置下界常量的类型说明
        TypeSpec minType = constantToken.getType() == IDENTIFIER
                               ? constantParser.getConstantType(constantToken)
                               : constantParser.getConstantType(minValue);
        minValue = checkValueType(constantToken, minValue, minType);
        token = currentToken();
        Boolean sawDotDot = false;
        //检查子界中表示范围的两个点
        if (token.getType() == DOT_DOT) {
            token = nextToken();
            sawDotDot = true;
        }
        TokenType tokenType = token.getType();
        // 上界常量？
        if (ConstantDefinitionsParser.CONSTANT_START_SET.contains(tokenType)) {
            if (!sawDotDot) {
                errorHandler.flag(token, MISSING_DOT_DOT, this);
            }
            // 解析上界
            token = synchronize(ConstantDefinitionsParser.CONSTANT_START_SET);
            constantToken = token;
            maxValue = constantParser.parseConstant(token);
            // 设置上界类型并检验是否与值匹配
            TypeSpec maxType = constantToken.getType() == IDENTIFIER
                               ? constantParser.getConstantType(constantToken)
                               : constantParser.getConstantType(maxValue);
            maxValue = checkValueType(constantToken, maxValue, maxType);
            //下界和上界的类型是否都有了？
            if ((minType == null) || (maxType == null)) {
                errorHandler.flag(constantToken, INCOMPATIBLE_TYPES, this);
            }
            //下界和上界类型是否一致（因为没有类型兼容，所以直接使用的比较）
            else if (minType != maxType) {
                errorHandler.flag(constantToken, INVALID_SUBRANGE_TYPE, this);
            }

            //下界大于上界的情况，报错
            else if ((minValue != null) && (maxValue != null) &&
                     ((Integer) minValue >= (Integer) maxValue)) {
                errorHandler.flag(constantToken, MIN_GT_MAX, this);
            }
        }
        else {
            errorHandler.flag(constantToken, INVALID_SUBRANGE_TYPE, this);
        }

        subrangeType.setAttribute(SUBRANGE_BASE_TYPE, minType);
        subrangeType.setAttribute(SUBRANGE_MIN_VALUE, minValue);
        subrangeType.setAttribute(SUBRANGE_MAX_VALUE, maxValue);

        return subrangeType;
    }

    /**
     * 检查一个类型说明的值，是否匹配
     */
    private Object checkValueType(Token token, Object value, TypeSpec type)
    {
        if (type == null) {
            return value;
        }
        if (type == Predefined.integerType) {
            return value;
        }
        else if (type == Predefined.charType) {
            char ch = ((String) value).charAt(0);
            return Character.getNumericValue(ch);
        }
        else if (type.getForm() == ENUMERATION) {
            return value;
        }
        else {
            errorHandler.flag(token, INVALID_SUBRANGE_TYPE, this);
            return value;
        }
    }
}
